/*
 * OrderService.java
 * Copyright (c) 2014, 2019, Kiri. 
 * All rights reserved.
 */
package com.onesalt.service;

import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.onesalt.common.ExceptionConstants;
import com.onesalt.domain.Dinner;
import com.onesalt.domain.Order;
import com.onesalt.domain.Payment;
import com.onesalt.domain.User;
import com.onesalt.utils.JSONUtil;
import com.onesalt.utils.NumberUtil;
import com.pingplusplus.model.Charge;

/**
 * 
 *
 * @author kiri
 * @date 2015年4月19日
 * @version 1.0
 */
@Service
@Transactional
public class OrderService extends AbstractService {

	private final OrderRepository orderRepository;
	private final DinnerRepository dinnerRepository;
	private final PaymentRepository paymentRepository;

	@Autowired
	public OrderService(OrderRepository orderRepository, DinnerRepository dinnerRepository, PaymentRepository paymentRepository) {
		this.orderRepository = orderRepository;
		this.dinnerRepository = dinnerRepository;
		this.paymentRepository = paymentRepository;
	}

	public OrderRepository getOrderRepository() {
		return orderRepository;
	}

	public DinnerRepository getDinnerRepository() {
		return dinnerRepository;
	}

	public PaymentRepository getPaymentRepository() {
		return paymentRepository;
	}

	public Order findRemindedOrder(long dinerId) {
		List<Order> orders = getOrderRepository().findByDinerIdAndDinnerStartTimeSpan(dinerId, new Date(), new Date(System.currentTimeMillis() + 3 * 86400));
		return orders != null && orders.size() > 0 ? orders.get(0) : null;
	}

	public List<Order> findLastOrdersByDinerId(long dinerId, long timeTillNow) {
		Date startTime = new Date(System.currentTimeMillis() / 1000 - timeTillNow);
		List<Order> orders = getOrderRepository().findLastOrdersByDinerId(dinerId, startTime);
		if (!CollectionUtils.isEmpty(orders)) {
			Collections.sort(orders, new Comparator<Order>() {
				public int compare(Order o1, Order o2) {
					if (o1.getDinner() == null || o1.getDinner().getStartTime() == null) {
						return 1;
					}
					if (o2.getDinner() == null || o2.getDinner().getStartTime() == null) {
						return -1;
					}
					return o2.getDinner().getStartTime().compareTo(o1.getDinner().getStartTime());
				}
			});
		}
		return orders;
	}

	public Order createOrder(Integer amount, Integer peopleNumber, Integer price, String notes, User diner, User cook, Dinner dinner) {
		Order order = new Order();
		order.setSn(generateSn(dinner));
		order.setAmount(amount);
		order.setPeopleNumber(peopleNumber);
		order.setPrice(price);
		order.setNotes(notes);
		order.setDiner(diner);
		order.setCook(cook);
		order.setDinner(dinner);
		order.setStatus(Order.STATUS_ORDERED);

		order = getOrderRepository().save(order);
		if (order != null) {
			dinner.setPeopleNumber(peopleNumber + getDinnerRepository().findPeopleNumberByDinnerId(dinner.getDinnerId()));
			getDinnerRepository().save(dinner);
		}
		return order;
	}
	
	public String generateSn(Dinner dinner) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		long year = cal.get(Calendar.YEAR) - 2000;
		long day = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(1970, 0, 1);
		long time = Math.abs(cal.getTimeInMillis() / 100L);
		String sn = year + NumberUtil.format(day, 3) + NumberUtil.format(time, 6) + NumberUtil.format(dinner.getDinnerId(), 3, 3)
				+ NumberUtil.format(dinner.getPeopleNumber(), 2, 2);
		return sn;
	}
	
	public Order createPaidOrder(Dinner dinner, User diner, User cook, Integer amount, Long dinerId,
			Integer peopleNumber, String notes, String chargeJson) {
		Charge charge = JSONUtil.fromJsonString(chargeJson, Charge.class);
		if (charge == null) {
			throw ExceptionConstants.ORDER_PAY_FAILED_EXCEPTION;
		}
		
		Order o = new Order();
		o.setAmount(amount);
		o.setPrice(dinner.getPrice());
		o.setPeopleNumber(peopleNumber);
		o.setDinnerId(dinner.getDinnerId());
		o.setDinerId(diner.getUserId());
		o.setCookId(cook.getUserId());
		o.setSn(charge.getOrderNo());
		o.setNotes(notes);
		o.setStatus(Order.STATUS_PAID);
		o = getOrderRepository().save(o);
		
		dinner.setPeopleNumber(o.getPeopleNumber() + dinner.getPeopleNumber());
		dinner.setAmount(amount + dinner.getAmount());
		getDinnerRepository().save(dinner);
		
		Payment pay = new Payment();
		pay.setOrderId(o.getOrderId());
		pay.setAmount(charge.getAmount());
		pay.setAmountSettle(charge.getAmountSettle());
		pay.setChannel(charge.getChannel());
		pay.setCharge(chargeJson);
		pay.setChargeId(charge.getId());
		pay.setClientIp(charge.getClientIp());
		if (charge.getCreated() != null) {
			pay.setCreateTime(new Date(charge.getCreated() * 1000));
		}
		pay.setCurrency(charge.getCurrency());
		if (charge.getTimeExpire() != null) {
			pay.setExpireTime(new Date(charge.getTimeExpire() * 1000));
		}
		if (charge.getTimePaid() != null) {
			pay.setPaidTime(new Date(charge.getTimePaid() * 1000));
		}
		if (charge.getTimeSettle() != null) {
			pay.setSettleTime(new Date(charge.getTimeSettle() * 1000));
		}
		pay.setFailureCode(charge.getFailureCode());
		pay.setFailureMsg(charge.getFailureMsg());
		pay.setLiveMode(charge.getLivemode());
		pay.setOrderNo(charge.getOrderNo());
		pay.setPaid(charge.getPaid());
		pay.setRefunded(charge.getRefunded());
		pay.setTransactionNo(charge.getTransactionNo());
		getPaymentRepository().save(pay);
		
		o.setDiner(diner);
		o.setCook(cook);
		o.setDinner(dinner);
		o.setPayment(pay);
		
		return o;
	}
}
